/**
 * Agent Designer Tests
 * 
 * Tests for agent configuration management
 */

import { describe, it, expect, beforeEach } from 'bun:test';
import { AgentConfigManager } from '../src/agent-designer/agent-config-manager';
import type { CreateAgentRequest, UpdateAgentRequest } from '../src/types/agent-config';

describe('AgentConfigManager', () => {
  let manager: AgentConfigManager;

  beforeEach(() => {
    manager = new AgentConfigManager();
  });

  describe('createAgent', () => {
    it('should create a new agent configuration', async () => {
      const request: CreateAgentRequest = {
        name: 'Test Agent',
        description: 'A test agent',
        systemPrompt: {
          type: 'text',
          content: 'You are a helpful assistant',
        },
        model: 'claude-sonnet-4',
      };

      const agent = await manager.createAgent(request);

      expect(agent.id).toBeDefined();
      expect(agent.name).toBe('Test Agent');
      expect(agent.description).toBe('A test agent');
      expect(agent.model).toBe('claude-sonnet-4');
      expect(agent.version).toBe(1);
      expect(agent.createdAt).toBeDefined();
      expect(agent.updatedAt).toBeDefined();
    });

    it('should create agent with default settings', async () => {
      const request: CreateAgentRequest = {
        name: 'Test Agent',
        description: 'A test agent',
        systemPrompt: {
          type: 'text',
          content: 'You are a helpful assistant',
        },
        model: 'claude-sonnet-4',
      };

      const agent = await manager.createAgent(request);

      expect(agent.settings.contextStrategy).toBe('compact');
      expect(agent.settings.maxTurns).toBe(10);
      expect(agent.settings.includePartialMessages).toBe(false);
    });

    it('should create agent with custom settings', async () => {
      const request: CreateAgentRequest = {
        name: 'Test Agent',
        description: 'A test agent',
        systemPrompt: {
          type: 'text',
          content: 'You are a helpful assistant',
        },
        model: 'claude-sonnet-4',
        settings: {
          contextStrategy: 'full',
          maxTurns: 20,
          temperature: 0.8,
        },
      };

      const agent = await manager.createAgent(request);

      expect(agent.settings.contextStrategy).toBe('full');
      expect(agent.settings.maxTurns).toBe(20);
      expect(agent.settings.temperature).toBe(0.8);
    });

    it('should reject invalid workflow', async () => {
      const request: CreateAgentRequest = {
        name: 'Test Agent',
        description: 'A test agent',
        systemPrompt: {
          type: 'text',
          content: 'You are a helpful assistant',
        },
        model: 'claude-sonnet-4',
        workflow: {
          nodes: [
            {
              id: 'node1',
              type: 'processing',
              position: { x: 0, y: 0 },
              data: { label: 'Process' },
            },
          ],
          edges: [
            {
              id: 'edge1',
              source: 'node1',
              target: 'nonexistent',
            },
          ],
        },
      };

      expect(async () => {
        await manager.createAgent(request);
      }).toThrow();
    });
  });

  describe('getAgent', () => {
    it('should retrieve an existing agent', async () => {
      const request: CreateAgentRequest = {
        name: 'Test Agent',
        description: 'A test agent',
        systemPrompt: {
          type: 'text',
          content: 'You are a helpful assistant',
        },
        model: 'claude-sonnet-4',
      };

      const created = await manager.createAgent(request);
      const retrieved = await manager.getAgent(created.id);

      expect(retrieved).toEqual(created);
    });

    it('should return null for non-existent agent', async () => {
      const agent = await manager.getAgent('nonexistent');
      expect(agent).toBeNull();
    });
  });

  describe('listAgents', () => {
    it('should list all agents', async () => {
      const request: CreateAgentRequest = {
        name: 'Test Agent',
        description: 'A test agent',
        systemPrompt: {
          type: 'text',
          content: 'You are a helpful assistant',
        },
        model: 'claude-sonnet-4',
      };

      await manager.createAgent(request);
      await manager.createAgent({ ...request, name: 'Test Agent 2' });

      const result = await manager.listAgents();

      expect(result.agents.length).toBe(2);
      expect(result.total).toBe(2);
    });

    it('should support pagination', async () => {
      const request: CreateAgentRequest = {
        name: 'Test Agent',
        description: 'A test agent',
        systemPrompt: {
          type: 'text',
          content: 'You are a helpful assistant',
        },
        model: 'claude-sonnet-4',
      };

      for (let i = 0; i < 5; i++) {
        await manager.createAgent({ ...request, name: `Test Agent ${i}` });
      }

      const page1 = await manager.listAgents(1, 2);
      expect(page1.agents.length).toBe(2);
      expect(page1.page).toBe(1);
      expect(page1.total).toBe(5);

      const page2 = await manager.listAgents(2, 2);
      expect(page2.agents.length).toBe(2);
      expect(page2.page).toBe(2);
    });
  });

  describe('updateAgent', () => {
    it('should update an existing agent', async () => {
      const request: CreateAgentRequest = {
        name: 'Test Agent',
        description: 'A test agent',
        systemPrompt: {
          type: 'text',
          content: 'You are a helpful assistant',
        },
        model: 'claude-sonnet-4',
      };

      const created = await manager.createAgent(request);

      // Wait a bit to ensure timestamp difference
      await new Promise(resolve => setTimeout(resolve, 10));

      const updateRequest: UpdateAgentRequest = {
        name: 'Updated Agent',
        description: 'An updated agent',
      };

      const updated = await manager.updateAgent(created.id, updateRequest);

      expect(updated.name).toBe('Updated Agent');
      expect(updated.description).toBe('An updated agent');
      expect(updated.version).toBe(2);
      expect(updated.updatedAt).toBeGreaterThanOrEqual(created.updatedAt);
    });

    it('should throw error for non-existent agent', async () => {
      const updateRequest: UpdateAgentRequest = {
        name: 'Updated Agent',
      };

      expect(async () => {
        await manager.updateAgent('nonexistent', updateRequest);
      }).toThrow();
    });
  });

  describe('deleteAgent', () => {
    it('should delete an existing agent', async () => {
      const request: CreateAgentRequest = {
        name: 'Test Agent',
        description: 'A test agent',
        systemPrompt: {
          type: 'text',
          content: 'You are a helpful assistant',
        },
        model: 'claude-sonnet-4',
      };

      const created = await manager.createAgent(request);
      const deleted = await manager.deleteAgent(created.id);

      expect(deleted).toBe(true);

      const retrieved = await manager.getAgent(created.id);
      expect(retrieved).toBeNull();
    });

    it('should return false for non-existent agent', async () => {
      const deleted = await manager.deleteAgent('nonexistent');
      expect(deleted).toBe(false);
    });
  });

  describe('duplicateAgent', () => {
    it('should duplicate an existing agent', async () => {
      const request: CreateAgentRequest = {
        name: 'Test Agent',
        description: 'A test agent',
        systemPrompt: {
          type: 'text',
          content: 'You are a helpful assistant',
        },
        model: 'claude-sonnet-4',
      };

      const original = await manager.createAgent(request);
      const duplicate = await manager.duplicateAgent(original.id);

      expect(duplicate.id).not.toBe(original.id);
      expect(duplicate.name).toBe('Test Agent (Copy)');
      expect(duplicate.description).toBe(original.description);
      expect(duplicate.model).toBe(original.model);
      expect(duplicate.version).toBe(1);
    });

    it('should duplicate with custom name', async () => {
      const request: CreateAgentRequest = {
        name: 'Test Agent',
        description: 'A test agent',
        systemPrompt: {
          type: 'text',
          content: 'You are a helpful assistant',
        },
        model: 'claude-sonnet-4',
      };

      const original = await manager.createAgent(request);
      const duplicate = await manager.duplicateAgent(original.id, 'Custom Name');

      expect(duplicate.name).toBe('Custom Name');
    });
  });

  describe('validateWorkflow', () => {
    it('should validate empty workflow', () => {
      const validation = manager.validateWorkflow({ nodes: [], edges: [] });
      expect(validation.valid).toBe(true);
      expect(validation.warnings.length).toBeGreaterThan(0);
    });

    it('should require input node', () => {
      const validation = manager.validateWorkflow({
        nodes: [
          {
            id: 'node1',
            type: 'processing',
            position: { x: 0, y: 0 },
            data: { label: 'Process' },
          },
        ],
        edges: [],
      });

      expect(validation.valid).toBe(false);
      expect(validation.errors.some(e => e.message.includes('input node'))).toBe(true);
    });

    it('should detect invalid edge references', () => {
      const validation = manager.validateWorkflow({
        nodes: [
          {
            id: 'node1',
            type: 'input',
            position: { x: 0, y: 0 },
            data: { label: 'Input' },
          },
        ],
        edges: [
          {
            id: 'edge1',
            source: 'node1',
            target: 'nonexistent',
          },
        ],
      });

      expect(validation.valid).toBe(false);
      expect(validation.errors.some(e => e.message.includes('non-existent'))).toBe(true);
    });
  });

  describe('templates', () => {
    it('should load default templates', async () => {
      const templates = await manager.getTemplates();
      expect(templates.length).toBeGreaterThan(0);
    });

    it('should create agent from template', async () => {
      const templates = await manager.getTemplates();
      const template = templates[0];

      const agent = await manager.createFromTemplate(template.id);

      expect(agent.name).toBe(template.config.name);
      expect(agent.model).toBe(template.config.model);
    });

    it('should create agent from template with custom name', async () => {
      const templates = await manager.getTemplates();
      const template = templates[0];

      const agent = await manager.createFromTemplate(template.id, 'Custom Name');

      expect(agent.name).toBe('Custom Name');
    });
  });
});

